package data

import (
	"context"
	"fmt"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"mig/pkg/chunk"
	"mig/pkg/types"
)

type MongoDB struct {
	client    *mongo.Client
	db        *mongo.Database
	cfg       *types.MongoConfig
	schema    *types.Schema
	algorithm types.Algorithm
}

func (m *MongoDB) Execute(sql string) error {
	//TODO implement me
	panic("implement me")
}

func (m *MongoDB) WriteChunk(chunk chunk.Chunk) error {
	//TODO implement me
	panic("implement me")
}

func (m *MongoDB) Count() (int64, error) {
	//TODO implement me
	panic("implement me")
}

func NewMongoDB(cfg *types.MongoConfig, schema *types.Schema, algorithm types.Algorithm) *MongoDB {
	uri := fmt.Sprintf("mongodb://%s:%s@%s:%s", cfg.Username, cfg.Password, cfg.Host, cfg.Port)
	clientOptions := options.Client().ApplyURI(uri)
	client, err := mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		panic(err)
	}

	db := client.Database(cfg.Database)
	return &MongoDB{client, db, cfg, schema, algorithm}
}

func (m *MongoDB) ReadChunk(chunkNo, chunkSize int64) (chunk.Chunk, error) {
	collection := m.db.Collection(m.schema.Name)
	begin := chunkNo*chunkSize + 1
	end := begin + chunkSize
	ctx := context.Background()

	filter := bson.M{
		m.schema.PrimaryField: bson.M{
			"$gte": begin,
			"$lt":  end,
		},
	}

	cursor, err := collection.Find(ctx, filter)
	if err != nil {
		return nil, err
	}
	defer cursor.Close(ctx)

	ck := chunk.NewChunk(m.algorithm)
	for cursor.Next(ctx) {
		var result bson.M
		if err = cursor.Decode(&result); err != nil {
			return nil, err
		}

		//for _, field := range m.schema.Fields {
		//	val := result[field.Name]
		//	err = utils.WriteBytes(chunk, val, &field)
		//	if err != nil {
		//		return nil, err
		//	}
		//}
	}
	ck.Finalize()

	return ck, nil
}

func (m *MongoDB) Identifier() string {
	return m.cfg.Identifier()
}
